package com.aps.algorithm.kpi.template;

import com.aps.algorithm.entity.ApsKpiTemplate;
import com.aps.algorithm.entity.BasicEquipSchedulingResult;
import com.aps.algorithm.kpi.excutor.KpiTemplateExcutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class DefaultApsKpiTemplateEngine implements KpiTemplateExcutor<List<BasicEquipSchedulingResult>> {

    public static final String METRIC_TOTAL_ORDER_COUNT = "total_order_count";
    public static final String METRIC_TOTAL_PROCESS_COUNT = "total_process_count";
    public static final String METRIC_AVG_PROCESS_DURATION = "avg_process_duration_minutes";
    public static final String METRIC_EQUIPMENT_UTILIZATION = "equipment_utilization";
    public static final String METRIC_AVG_EQUIPMENT_UTILIZATION = "avg_equipment_utilization";

    @Override
    public Map<String, Object> applay(List<BasicEquipSchedulingResult> data, List<ApsKpiTemplate> templates) {
        if (CollectionUtils.isEmpty(data)) {
            log.warn("排程数据为空，无法计算 KPI 指标");
            return Collections.emptyMap();
        }
        //指标对应的默认参数
//        List<KpiMetricDefinition> metricDefinitions = resolveMetrics(template);
        Map<String, Object> result = new LinkedHashMap<>();
        //do cal
        for (ApsKpiTemplate definition : templates) {
            if (definition == null || !StringUtils.equals("1",definition.getEnabled()) || StringUtils.isBlank(definition.getTemplateCode())) {
                continue;
            }
            switch (definition.getTemplateCode()) {
                case METRIC_TOTAL_ORDER_COUNT -> result.put(definition.getTemplateCode(), calculateTotalOrderCount(data));
                case METRIC_TOTAL_PROCESS_COUNT -> result.put(definition.getTemplateCode(), calculateTotalProcessCount(data));
                case METRIC_AVG_PROCESS_DURATION -> result.put(definition.getTemplateCode(), calculateAverageProcessDurationMinutes(data));
                case METRIC_EQUIPMENT_UTILIZATION -> result.put(definition.getTemplateCode(), calculateEquipmentUtilization(data));
                case METRIC_AVG_EQUIPMENT_UTILIZATION -> result.put(definition.getTemplateCode(), calculateAverageEquipmentUtilization(data));
                default -> log.info("默认模板暂未实现指标计算: {}", definition.getTemplateCode());
            }
        }
        return result;
    }

    //计划包含的订单数量
    private long calculateTotalOrderCount(List<BasicEquipSchedulingResult> data) {
        return data.stream()
                .map(BasicEquipSchedulingResult::getOrderId)
                .filter(StringUtils::isNotBlank)
                .distinct()
                .count();
    }

    private long calculateTotalProcessCount(List<BasicEquipSchedulingResult> data) {
        return data.stream()
                .map(BasicEquipSchedulingResult::getProcessId)
                .filter(StringUtils::isNotBlank)
                .count();
    }

    private BigDecimal calculateAverageProcessDurationMinutes(List<BasicEquipSchedulingResult> data) {
        return KpiMathUtils.avg_process(data);
    }

    private Map<String, BigDecimal> calculateEquipmentUtilization(List<BasicEquipSchedulingResult> data) {
        return KpiMathUtils.use_equipt_percent(data);
    }

    private BigDecimal calculateAverageEquipmentUtilization(List<BasicEquipSchedulingResult> data) {
        Map<String, BigDecimal> utilization = calculateEquipmentUtilization(data);
        if (utilization.isEmpty()) {
            return BigDecimal.ZERO;
        }
        BigDecimal sum = utilization.values().stream()
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        return sum.divide(BigDecimal.valueOf(utilization.size()), 4, RoundingMode.HALF_UP);
    }

    private long calculateDurationMinutes(BasicEquipSchedulingResult record) {
        LocalDateTime start = record.getProcessStartTime();
        LocalDateTime end = record.getProcessEndTime();
        if (start == null || end == null || !end.isAfter(start)) {
            return 0;
        }
        return Duration.between(start, end).toMinutes();
    }
}

